package org.dromara.netbar.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.constant.NetbarConstants;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.AssertUtils;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.RedisKeyGeneratorUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.netbar.domain.NetbarClient;
import org.dromara.netbar.domain.NetbarMember;
import org.dromara.netbar.domain.bo.NetbarAmountBo;
import org.dromara.netbar.domain.bo.NetbarCouponMemberBo;
import org.dromara.netbar.domain.bo.NetbarMemberBo;
import org.dromara.netbar.domain.bo.NetbarMemberLoginBo;
import org.dromara.netbar.domain.vo.*;
import org.dromara.netbar.mapper.NetbarClientMapper;
import org.dromara.netbar.mapper.NetbarMemberMapper;
import org.dromara.netbar.service.*;
import org.dromara.netbar.util.BkdUtils;
import org.dromara.netbar.util.CouponUtils;
import org.dromara.system.service.ISysConfigService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Stream;

/**
 * 会员逻辑服务类
 * @author ZhouWenTao
 * @create 2025-07-23 15:01
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class MemberService {
    @Resource
    private final NetbarClientMapper netbarClientMapper;
    @Resource
    private final NetbarMemberMapper netbarMemberMapper;
    private final INetbarMemberService netbarMemberService;
    private final INetbarClientService netbarClientService;
    private final INetbarMemberLevelService netbarMemberLevelService;
    private final INetbarCouponMemberService netbarCouponMemberService;
    private final INetbarSeatTypeService netbarSeatTypeService;
    private final INetbarAutoGrantService netbarAutoGrantService;
    private final CouponService couponService;
    private final NetbarCommonService netbarCommonService;
    /**
     * 会员激活状态
     * @param bo
     * @return
     */
    public Map<String, Object> openClient(NetbarMemberLoginBo bo) {
        // 建立电脑链接
        Long memberId = bo.getMemberId();
        Long clientId = bo.getClientId();
        long currentTimeMillis = System.currentTimeMillis();
        // 1、判断是否可以用
        AssertUtils.notNull(memberId, "会员ID不能为空");
        AssertUtils.notNull(clientId, "座位ID不能为空");
        // 用户信息
        NetbarMemberVo netbarMemberVo = netbarMemberService.queryById(memberId);
        AssertUtils.notNull(netbarMemberVo, "会员信息不存在");
        // 座位信息
        NetbarClientVo netbarClientVo = netbarClientService.queryById(clientId);
        AssertUtils.notNull(netbarClientVo, "座位信息不存在");
        AssertUtils.isTrue("0".equals(netbarClientVo.getStatus()), "座位不可用");
        AssertUtils.notTrue("0".equals(netbarMemberVo.getStatus()), "账号需要先在柜台进行上机");
        AssertUtils.notTrue("1".equals(netbarMemberVo.getStatus()), "当前账号已在其他座位登录");
        AssertUtils.notTrue("2".equals(netbarMemberVo.getStatus()), "当前账号已在其他座位挂机");
        // 自动使用用户的代金券
        couponService.useAutoCoupon(memberId, NetbarConstants.Coupon.TYPE_1);
        // 2、获取配置信息
        String tenantId = netbarMemberVo.getTenantId();
        Long seatTypeId = netbarClientVo.getSeatTypeId();
        // Redis-Key
        String memberRedisKey = RedisKeyGeneratorUtils.getOnlineMemberKey(tenantId);
        String clientRedisKey = RedisKeyGeneratorUtils.getOnlineClientKey(tenantId);
        Map<String, Object> memberMap = RedisUtils.getCacheMapValue(memberRedisKey, memberId.toString());
        AssertUtils.isNull(memberMap, "当前用户已登录电脑");
        // 费率（默认 1）
        long discountRate = NetbarConstants.Number.DEFAULT_DISCOUNT_RATE;
        // 根据用户等级和座位类型获取价格
        Long memberLevelPrice = netbarSeatTypeService.getSeatPriceByMemberLevelAndSeatType(netbarMemberVo.getMemberLevelId(), netbarClientVo.getSeatTypeId());
        NetbarSeatTypeVo netbarSeatTypeVo = netbarSeatTypeService.queryById(netbarClientVo.getSeatTypeId());
        NetbarMemberLoginBo netbarMemberBo = new NetbarMemberLoginBo(){{
            setMemberId(memberId);
            setTenantId(tenantId);
            setSeatTypeId(seatTypeId);
        }};
        // 获取可以自动使用卡券
        List<NetbarCouponMemberVo> validCoupons = couponService.getValidCoupons(netbarMemberBo, true);
        Map<String, NetbarCouponMemberVo> validCouonsToSeatTypeNameMap = CouponService.getValidCouonsToSeatTypeNameMap(validCoupons);

        // 折扣券信息
        NetbarCouponVo couponZkq = null;

        // 1、获取包夜券信息
        NetbarCouponVo couponBsq = RedisUtils.getCacheMapValue(RedisKeyGeneratorUtils.getMemberUseCouponKey(tenantId), RedisKeyGeneratorUtils.getMemberUseCouponHKey(memberId, NetbarConstants.Coupon.TYPE_3));

        boolean isFree = false;
        // 判断包时券是否可用
        if (null != couponBsq &&  (0 == couponBsq.getValidArea()  ||  seatTypeId.equals(couponBsq.getValidArea()))) {
            isFree = CouponUtils.checkCouponBsValid(couponBsq, currentTimeMillis);
        }else{
            couponBsq = null;
        }

        // 包时
        couponBsq = RedisUtils.getCacheMapValue(RedisKeyGeneratorUtils.getMemberUseCouponKey(tenantId), RedisKeyGeneratorUtils.getMemberUseCouponHKey(memberId, NetbarConstants.Coupon.TYPE_2));
        // 判断包时券是否可用
        if (null != couponBsq &&  (0 == couponBsq.getValidArea()  ||  seatTypeId.equals(couponBsq.getValidArea()))) {
            isFree = CouponUtils.checkCouponBsValid(couponBsq, currentTimeMillis);
        }else{
            couponBsq = null;
        }

        if(!isFree){
            // 看看有没有能用的包时券
            if (CollectionUtil.isNotEmpty(validCouonsToSeatTypeNameMap)) {
                // 获取包时券信息
                NetbarCouponMemberVo couponMemberVo = validCouonsToSeatTypeNameMap.get(NetbarConstants.Coupon.TYPE_3 +  "_" + seatTypeId);
                if (null == couponMemberVo) {
                    couponMemberVo = validCouonsToSeatTypeNameMap.get(NetbarConstants.Coupon.TYPE_3 + "_0"); // 通用区域
                }
                if (null == couponMemberVo) {
                    couponMemberVo = validCouonsToSeatTypeNameMap.get(NetbarConstants.Coupon.TYPE_2 + "_" + seatTypeId);
                }
                if (null == couponMemberVo) {
                    couponMemberVo = validCouonsToSeatTypeNameMap.get(NetbarConstants.Coupon.TYPE_2 +"_0"); // 通用区域
                }
                if(null != couponMemberVo){
                    // 使用 包时券
                    NetbarCouponMemberBo netbarCouponMemberBo = new NetbarCouponMemberBo();
                    netbarCouponMemberBo.setCouponId(couponMemberVo.getCouponId());
                    netbarCouponMemberBo.setMemberId(memberId);
                    couponService.useCoupon(netbarCouponMemberBo);
                    // 获取包时券信息
                    couponBsq = RedisUtils.getCacheMapValue(RedisKeyGeneratorUtils.getMemberUseCouponKey(tenantId), RedisKeyGeneratorUtils.getMemberUseCouponHKey(memberId, couponMemberVo.getCoupon().getCouponType()));
                }
            }

            if (null != couponBsq &&  (0 == couponBsq.getValidArea()  ||  seatTypeId.equals(couponBsq.getValidArea()))) {
                isFree = CouponUtils.checkCouponBsValid(couponBsq, currentTimeMillis);
            }else{
                couponBsq = null;

                if (CollectionUtil.isNotEmpty(validCouonsToSeatTypeNameMap)) {
                    // 获取折扣券
                    NetbarCouponMemberVo couponMemberVo = validCouonsToSeatTypeNameMap.get("折扣券_" + seatTypeId);
                    if(null == couponMemberVo){
                        couponMemberVo = validCouonsToSeatTypeNameMap.get("折扣券_0"); // 通用区域
                    }
                    if(null != couponMemberVo){
                        // 使用
                        NetbarCouponMemberBo netbarCouponMemberBo = new NetbarCouponMemberBo();
                        netbarCouponMemberBo.setCouponId(couponMemberVo.getCouponId());
                        netbarCouponMemberBo.setMemberId(memberId);
                        couponService.useCoupon(netbarCouponMemberBo);
                    }
                }

                // 使用折扣券
                currentTimeMillis = System.currentTimeMillis(); // 刷新时间
                // 获取折扣券信息
                couponZkq = RedisUtils.getCacheMapValue(RedisKeyGeneratorUtils.getMemberUseCouponKey(tenantId), RedisKeyGeneratorUtils.getMemberUseCouponHKey(memberId, NetbarConstants.Coupon.TYPE_4));
                // 如果没有使用包时券/包时券过期，则使用 折扣券
                if (null != couponZkq && (0 == couponZkq.getValidArea().longValue() || seatTypeId == couponZkq.getValidArea().longValue())) {
                    // 注意折扣券没有使用时长范围，只有日期之间的有效期
                    if (2 == couponZkq.getValidTermType()) {
                        // 有效期检测
                        boolean isDateValid = currentTimeMillis >= couponZkq.getValidDateStart().getTime()
                            && currentTimeMillis <= couponZkq.getValidDateEnd().getTime();
                        if (isDateValid) {
                            discountRate = couponZkq.getDiscountRate();
                        }
                    }else{
                        // 永久时间范围
                        discountRate = couponZkq.getDiscountRate();
                    }
                }

            }
        }

        long timeFlowRate = getTimeFlowRate(tenantId);
        Long timeDeductRate = getTimeDeductRate(tenantId);
        long basePrice = (null != memberLevelPrice && 0 != memberLevelPrice) ? memberLevelPrice : netbarSeatTypeVo.getBaseRate();
        // 下次计费时间
        long nextTimeMillis = getNextTime(currentTimeMillis, timeFlowRate, timeDeductRate);
        // 下次计费价格
        // 小时价
        long oneTimePrice = checkRateAmount(basePrice, discountRate);
        // 单次扣费金额
        long onceTimePriceLong = (basePrice / NetbarConstants.Number.TIME_COUNT_DEDUCTION);
        onceTimePriceLong = checkRateAmount(onceTimePriceLong, discountRate);
        // 扣费一次
        if (!isFree) {
            netbarMemberVo = netbarMemberService.deductBalance(memberId, onceTimePriceLong);
        }
        // 3、写入redis
        Map<String, Object> data = new HashMap<>();
        data.put("startTime", currentTimeMillis);
        data.put("nextTime", nextTimeMillis);
        data.put("memberId", memberId);
        data.put("clientId", clientId);
        data.put("account", netbarMemberVo.getAccount());
        data.put("memberName", netbarMemberVo.getMemberName());
        data.put("clientNo", netbarClientVo.getClientNo());
        data.put("seatTypeName", netbarSeatTypeVo.getSeatTypeName());
        data.put("seatTypeId", netbarClientVo.getSeatTypeId());
        data.put("timeFlowRate",timeFlowRate); // 时间速率（秒）
        data.put("timeDeductRate", timeDeductRate); // 扣费次数
        data.put("baseBalance", netbarMemberVo.getBaseBalance()); // 基础余额
        data.put("awardBalance", netbarMemberVo.getAwardBalance()); // 奖励余额
        // 已扣费
        data.put("deductBalance", 0L); // 已扣费
        data.put("basePrice", basePrice); // 座位基础费率
        data.put("oneTimePrice", oneTimePrice); // 小时价
        data.put("memberLevelId", netbarMemberVo.getMemberLevelId());
        data.put("memberLevelName", netbarMemberVo.getMemberLevelName()); // 会员等级名称
        data.put("couponBsq", couponBsq);
        data.put("couponZkq", couponZkq);
        data.put("macAddress", bo.getMacAddress());
        data.put("lastLoginTime", netbarMemberVo.getLastLoginTime()); // 最后登录时间
        data.put("registerTime", netbarMemberVo.getCreateTime()); // 注册时间
        // ===========店铺信息
        Map<String, Object> store = new HashMap<>();
        store.put("storeName", netbarCommonService.getStoreName(tenantId));
        data.put("store", store);
        // ======================
        RedisUtils.setCacheMapValue(memberRedisKey, memberId.toString(), data);
        RedisUtils.setCacheMapValue(clientRedisKey, clientId.toString(), data);
        // 修改会员状态为在线
        Boolean memberChangeStatusFlag = netbarMemberService.changeStatus(new NetbarMemberBo() {{
            setMemberId(bo.getMemberId());
            setStatus("1");
        }});
        if (memberChangeStatusFlag) {
            // 修改座位状态
            netbarClientMapper.update(new LambdaUpdateWrapper<NetbarClient>().eq(NetbarClient::getClientId, bo.getClientId()).set(NetbarClient::getStatus, "1"));
            return data;
        }
        return null;
    }

    /**
     * 检查在线会员有效期（扣费）
     */
    @Transactional(rollbackFor = Exception.class)
    public Long checkOneMember(Map<String, Object> member, String tenantId, long currentTime, boolean offline) {
        if (null == member) {
            return 0L;
        }

        // 使用 getOrDefault 避免空指针，并提供默认值
        long memberId = (Long) member.get("memberId");
        long clientId = (Long) member.get("clientId");
        long seatTypeId = (Long) member.get("seatTypeId");
        if (0 == memberId || 0 == clientId || 0 == seatTypeId) {
            return 0L;
        }

        long baseBalance = (Long) member.getOrDefault("baseBalance", 0L);
        long awardBalance = (Long) member.getOrDefault("awardBalance", 0L);
        long amount = baseBalance + awardBalance; // 当前余额（分）
        long timeFlowRate = (Long) member.getOrDefault("timeFlowRate", getTimeFlowRate(tenantId)); // 结算一小时所需的秒数 (例如: 3600)
        long timeDeductRate = (Long) member.getOrDefault("timeDeductRate", getTimeDeductRate(tenantId)); // 结算一小时所扣费次数
        long basePrice = (Long) member.get("basePrice"); // 每小时费率（分）
        long deductBalance = (Long) member.get("deductBalance"); // 已扣费金额（分）
        long startTime = (Long) member.get("startTime"); // 上机时间戳（毫秒）
        long nextTime = (Long) member.get("nextTime"); // 下次计费时间戳（毫秒）
        // 费率（默认 1）
        long discountRate = NetbarConstants.Number.DEFAULT_DISCOUNT_RATE;
        if (0 == timeFlowRate || 0 == basePrice || 0 == startTime || 0 == nextTime) {
            // 数据不完整，跳过此用户
            log.warn("在线会员数据不完整，memberId: {}", memberId);
            return -1L;
        }

        long currentTimeMillis = System.currentTimeMillis();
        boolean isFree = false;

        // 优先判断包夜券>包时券>折扣券
        // 包夜券
        NetbarCouponVo couponBsq = RedisUtils.getCacheMapValue(
            RedisKeyGeneratorUtils.getMemberUseCouponKey(tenantId),
            RedisKeyGeneratorUtils.getMemberUseCouponHKey(memberId, NetbarConstants.Coupon.TYPE_3)
        );
        if (null != couponBsq) {
            // 处理指定日期券
            isFree = CouponUtils.checkCouponBsValid(couponBsq, currentTimeMillis);
        }

        if(!isFree){
            // 包时券
            couponBsq = RedisUtils.getCacheMapValue(
                RedisKeyGeneratorUtils.getMemberUseCouponKey(tenantId),
                RedisKeyGeneratorUtils.getMemberUseCouponHKey(memberId, NetbarConstants.Coupon.TYPE_2)
            );
        }
        if (null != couponBsq) {
            // 处理指定日期券
            isFree = CouponUtils.checkCouponBsValid(couponBsq, currentTimeMillis);
        }

        // 防止卡券不可以也返回参数
        if(!isFree){
            couponBsq = null;
        }

        NetbarCouponVo couponZkq = null;
        if(!isFree){
            couponBsq = null;
            // 没有包时券，查询折扣券
            couponZkq = RedisUtils.getCacheMapValue(RedisKeyGeneratorUtils.getMemberUseCouponKey(tenantId), RedisKeyGeneratorUtils.getMemberUseCouponHKey(memberId, NetbarConstants.Coupon.TYPE_4));
            if (null != couponZkq && (0 == couponZkq.getValidArea().longValue() || seatTypeId == couponZkq.getValidArea().longValue())) {
                // 注意折扣券没有使用时长范围，只有日期之间的有效期
                if (2 == couponZkq.getValidTermType()) {
                    // 有效期检测
                    boolean isDateValid = currentTimeMillis >= couponZkq.getValidDateStart().getTime()
                        && currentTimeMillis <= couponZkq.getValidDateEnd().getTime();
                    if (isDateValid) {
                        discountRate = couponZkq.getDiscountRate();
                    }
                }else{
                    // 永久时间范围
                    discountRate = couponZkq.getDiscountRate();
                }
            }
        }
        // 小时价
        long oneTimePrice = checkRateAmount(basePrice, discountRate);
        // 跳点价
        long onceTimePriceLong = (basePrice / NetbarConstants.Number.TIME_COUNT_DEDUCTION); // 会员价/跳点次数
        onceTimePriceLong = checkRateAmount(onceTimePriceLong, discountRate);

        member.put("couponZkq", couponZkq); // 折扣券
        member.put("couponBsq", couponBsq); // 包时券
        if(!offline){
            if(nextTime <= currentTime){
                // 不是手动下线
                //TODO 扣费逻辑
                member.put("nextTime", getNextTime(nextTime, timeFlowRate, timeDeductRate));
                // 上次扣费时间
                member.put("lastTime", nextTime);
                if (!isFree) {
                    // 扣费
                    NetbarMemberVo netbarMemberVo = netbarMemberService.deductBalance(memberId, onceTimePriceLong);
                    amount = netbarMemberVo.getBaseBalance() + netbarMemberVo.getAwardBalance();
                    if (amount <= onceTimePriceLong) {
                        // 余额不足
                        // TODO 通知，下线
                        // 给Socket 发送客户余额
                        BkdUtils.lockscreen((String) member.get("macAddress"));
                        netbarMemberService.offline(new NetbarMemberBo() {{
                            setMemberId(memberId);
                        }});
                        return memberId;
                    }
                    member.put("deductBalance", deductBalance + onceTimePriceLong);
                    member.put("oneTimePrice", oneTimePrice);
                    member.put("baseBalance", netbarMemberVo.getBaseBalance());
                    member.put("awardBalance", netbarMemberVo.getAwardBalance());
                    // 给Socket 发送客户余额
                    String jsonStr = JsonUtils.toJsonString(member);
                    BkdUtils.sendMemberInfo((String) member.get("macAddress"), jsonStr);
                    // BkdUtils.sendBalance((String) member.get("macAddress"), netbarMemberVo.getBaseBalance(), netbarMemberVo.getAwardBalance());
                }
            }else{
                // 非手动下线
                // 检测余额是否够下次计费
                if (amount <= onceTimePriceLong && !isFree) {
                    // 余额不足
                    BkdUtils.sendMsg((String) member.get("macAddress"),"余额即将不足");
                    // TODO 通知，余额即将不足
                    return memberId;
                }
            }
            RedisUtils.setCacheMapValue(RedisKeyGeneratorUtils.getOnlineMemberKey(tenantId), memberId + "", member);
        } else {
            // 手动下线
            netbarMemberService.offline(new NetbarMemberBo() {{
                setMemberId(memberId);
            }});
        }
        return memberId;
    }

    /**
     * 会员平台充值
     * @param bo {amount: 100(单位:分),memberId:会员ID,paymentMethod:支付方式,}
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void rechargeBalance(NetbarAmountBo bo) {
        Long amount = bo.getAmount();
        String paymentMethod = bo.getPaymentMethod();
        Long memberId = bo.getMemberId();
        Date nowDate = new Date();
        // 1、参数校验
        AssertUtils.notNull(memberId, "会员ID不能为空");
        AssertUtils.notNull(amount, "充值金额不能为空");
        AssertUtils.notNull(paymentMethod, "支付方式不能为空");
        AssertUtils.notTrue(amount > 1000000, "充值金额不能大于10000元");
        AssertUtils.notTrue(amount < 100, "充值金额不能小于1元");
        // 2、获取会员信息
        NetbarMemberVo netbarMemberVo = netbarMemberService.queryById(memberId);
        AssertUtils.notNull(netbarMemberVo, "会员信息不存在");
        // AssertUtils.notTrue("0".equals(netbarMemberVo.getStatus()), "当前账号不可充值");
        Long baseBalance = netbarMemberVo.getBaseBalance();
        // 用户使用的卡券
        List<Long> useCouponMemberIds = bo.getUseCouponMemberIds();
        if(CollectionUtil.isNotEmpty(useCouponMemberIds)){
            try {
                List<NetbarCouponMemberVo> couponMemberVoList = netbarCouponMemberService.listCouponMemberByCouponMemberIds(useCouponMemberIds, NetbarConstants.Coupon.TYPE_6);
                if (!couponMemberVoList.isEmpty()) {
                    couponMemberVoList = couponMemberVoList.stream().filter(item -> memberId.equals(item.getMemberId())).toList();
                    R<?> r;
                    for (NetbarCouponMemberVo couponMemberVo : couponMemberVoList) {
                        r = couponService.useCoupon(new NetbarCouponMemberBo() {{
                            setMemberId(memberId);
                            setCouponId(couponMemberVo.getCouponId());
                            setUseTime(nowDate);
                            setUseDjFlag(true);
                        }});
                        if (r.getCode() == 200) {
                            amount+= couponMemberVo.getCoupon().getCouponAmount();
                        }
                    }
                }
            }catch (Exception e){
                throw new RuntimeException("使用代金券处理失败,请联系管理员处理!");
            }
        }
        // TODO 会员升级
        // 检测充值余额 是否满足会员自动升级
        NetbarMemberLevelVo memberLevelVo = netbarMemberLevelService.getMemberLevelByAutoBalance(netbarMemberVo.getTenantId(), amount);
        // TODO 微信，支付宝介入
        String memberRedisKey = netbarMemberVo.getTenantId() + NetbarConstants.ONLINE_MEMBER_KEY;
        Map<String, Object> onlineMemberMap = RedisUtils.getCacheMapValue(memberRedisKey, memberId.toString());
        // 3、充值
        netbarMemberService.addBalance(memberId, amount, 1L); // 添加充值金额变动记录
        baseBalance += amount;
        LambdaUpdateWrapper<NetbarMember> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.eq(NetbarMember::getMemberId, memberId);
        if ("activation".equals(bo.getAfterMethod())) {
            // 如果是待机状态，充值后操作 激活 待上机 状态
            if (NetbarConstants.Member.STATUS_NORMAL.equals(netbarMemberVo.getStatus())) {
                lambdaUpdateWrapper.set(NetbarMember::getStatus, NetbarConstants.Member.STATUS_WAIT_OPEN);
            }
        }else if("levelUp".equals(bo.getAfterMethod())){
            // 如果自动升级
            if (null == netbarMemberVo.getMemberLevelId() || null != memberLevelVo && netbarMemberLevelService.compareMemberLevelAmount(netbarMemberVo.getMemberLevelId(), memberLevelVo.getLevelId())) {
                // 判断 会员等级是否高于当前会员等级
                lambdaUpdateWrapper.set(NetbarMember::getMemberLevelId, memberLevelVo.getLevelId());
                lambdaUpdateWrapper.set(NetbarMember::getMemberLevelTime, new Date());
                // 获取会员的费用折扣
                if (null != onlineMemberMap) {
                    Long seatTypeId = (Long) onlineMemberMap.get("seatTypeId");
                    Long memberPrice = netbarSeatTypeService.getSeatPriceByMemberLevelAndSeatType(memberLevelVo.getLevelId(), seatTypeId);
                    onlineMemberMap.put("basePrice", memberPrice);
                    onlineMemberMap.put("memberLevelName", memberLevelVo.getLevelName());
                }
            }
        }
        lambdaUpdateWrapper.set(NetbarMember::getUpdateTime, nowDate);
        netbarMemberMapper.update(lambdaUpdateWrapper);
        // 如果有自动发放的礼包
        List<NetbarAutoGrantVo> netbarAutoGrantVos = netbarAutoGrantService.listGiftPackByBalanceAndMemberId(amount, netbarMemberVo.getTenantId(), memberId);
        if(CollectionUtil.isNotEmpty(netbarAutoGrantVos)){
            // 发放礼包
            netbarAutoGrantService.sendGiftPack(netbarAutoGrantVos, netbarMemberVo);
        }
        // 4、判断是否在线，如果在线 修改一下redis中的余额
        if (null != onlineMemberMap && !onlineMemberMap.keySet().isEmpty()) {
            onlineMemberMap.put("baseBalance", baseBalance);
            onlineMemberMap.put("awardBalance", netbarMemberVo.getAwardBalance());
//            onlineMemberMap.put("memberLevelName", "普通会员"); // 获取会员等级名称
            RedisUtils.setCacheMapValue(memberRedisKey, memberId.toString(), onlineMemberMap);
            // 给Socket 发送客户余额
            BkdUtils.sendBalance((String) onlineMemberMap.get("macAddress"), baseBalance, netbarMemberVo.getAwardBalance());
        }
    }

    public Long getTimeFlowRate(String tenantId){
        if (StringUtils.isBlank(tenantId)) {
            return 3600L;
        }
        String timeFlowRate = RedisUtils.getCacheMapValue(tenantId + ":"+ CacheNames.SYS_CONFIG,  NetbarConstants.TIME_FLOW_RATE_KEY);
        if (null == timeFlowRate) {
            return 3600L;
        }
        return Long.parseLong(timeFlowRate);
    }

    public Long getTimeDeductRate(String tenantId){
        if (StringUtils.isBlank(tenantId)) return 10L;
        String timeDeductRate = RedisUtils.getCacheMapValue(tenantId + ":"+ CacheNames.SYS_CONFIG,  NetbarConstants.TIME_DEDUCT_RATE_KEY);
        if (null == timeDeductRate) return 10L;
        return Long.parseLong(timeDeductRate);
    }


    /**
     * 添加临时会员
     */
    @Transactional
    public String addTempMember(NetbarMemberBo bo) {
        System.currentTimeMillis();
        String account = RandomUtil.randomNumbers(18);
        bo.setMemberType("temp");

        bo.setAccount( account);
        netbarMemberService.insertByBo(bo);
        return null;
    }

    /**
     * 下次计费时间
     */
    public Long getNextTime(long currentTimeMillis, long timeFlowRate, long timeDeductRate) {
        // 根据时间流逝速度，设置下次计费时间，如：1小时计费12次，隔5分钟计费一次
        double timeFlowRateLong = (double) timeFlowRate / timeDeductRate;// 间隔时间
        return currentTimeMillis + (long) (timeFlowRateLong * 1000);
    }


    /**
     * 价格折扣计算（分单位，防止出现三位小数金额）
     * @param amount 金额（分单位）
     * @param discount 折扣（分单位，如：75折=75，7折=70，原价=100）
     */
    public static long checkRateAmount(long amount, long discount){
        // 向上取整（平台多赚1分）
        return (amount * discount + 99) / 100;
        // 浮动，5舍6入
        /*long rawValue = amount * discount;  // 70*75=5250
        long remainder = rawValue % 100;   // 5250%100=50（判断是否正好0.5）
        long finalPrice;
        if (remainder == 50) {
            // 0.5分时向最近的偶数舍入
            finalPrice = (rawValue / 100) + ((rawValue / 100) % 2 == 1 ? 1 : 0);
        } else {
            // 否则标准四舍五入
            finalPrice = (rawValue + 50) / 100;
        }
        return finalPrice;*/
    }

    public void refreshMemberRedis(String tenantId, Long memberId) {
        // 获取之前的Redis
        String onlineMemberKey = RedisKeyGeneratorUtils.getOnlineMemberKey(tenantId);
        Map<String, Object> member =RedisUtils.getCacheMapValue(onlineMemberKey, memberId.toString());
        if (null != member && !member.keySet().isEmpty()) {
            // 获取会员包时券
            NetbarCouponVo couponBsq = RedisUtils.getCacheMapValue(
                RedisKeyGeneratorUtils.getMemberUseCouponKey(tenantId),
                RedisKeyGeneratorUtils.getMemberUseCouponHKey(memberId, NetbarConstants.Coupon.TYPE_2)
            );
            if (null == couponBsq) {
                // 处理时段券
                couponBsq = RedisUtils.getCacheMapValue(
                    RedisKeyGeneratorUtils.getMemberUseCouponKey(tenantId),
                    RedisKeyGeneratorUtils.getMemberUseCouponHKey(memberId, NetbarConstants.Coupon.TYPE_3)
                );
            }
            NetbarCouponVo couponZkq = null;
            if (null == couponBsq) {
                // 处理折扣券
                couponZkq = RedisUtils.getCacheMapValue(
                    RedisKeyGeneratorUtils.getMemberUseCouponKey(tenantId),
                    RedisKeyGeneratorUtils.getMemberUseCouponHKey(memberId, NetbarConstants.Coupon.TYPE_4)
                );
            }
            member.put("couponBsq", couponBsq);
            member.put("couponZkq", couponZkq);
            String memberLevelName = netbarMemberService.getMemberLevelName(memberId);
            if(null != memberLevelName){
                member.put("memberLevelName", memberLevelName);
            }
            // 重写Redis
            RedisUtils.setCacheMapValue(onlineMemberKey, memberId.toString(), member);
            // 给Socket 发送客户余额
            String jsonStr = JsonUtils.toJsonString(member);
            BkdUtils.sendMemberInfo((String) member.get("macAddress"), jsonStr);
        }
    }

}
